import { GLOBAL_APP } from "../globals";
import { SOUNDS } from "../sound/interface";
import { createLogger } from "./logging";
import { Signal } from "./signal";
import { Vector } from "./vector";

const logger = createLogger("click_detector");

const clickDetectorGlobals = {
  lastTouchTime: -1000
}

export class ClickDetector {
  constructor(
    element,
    {
      // 禁止默认事件
      preventDefault = true,
      // 点击添加的类
      applyCssClass = "pressed",
      // 点击音乐
      clickSound = SOUNDS.uiClick,
      // 点击偏移
      maxDistance = 20,
      // 是否捕获移动事件
      captureTouchmove = false,
      // 禁止点击事件
      preventClick = false,
      // 元素指定，不能为子元素
      targetOnly = false,
      // 长按时间
      longPressMs = 0
    }
  ) {
    assert(element, "元素不存在");

    this.preventDefault = preventDefault;
    this.applyCssClass = applyCssClass;
    this.clickSound = clickSound;
    this.maxDistance = maxDistance;
    this.captureTouchmove = captureTouchmove;
    this.preventClick = preventClick;
    this.targetOnly = targetOnly;
    this.longPressMs = longPressMs;

    this.click = new Signal();
    this.touchstart = new Signal();
    this.touchmove = new Signal();
    this.touchend = new Signal();
    this.touchout = new Signal();
    this.longPress = new Signal();

    this.clickStartTime = null;
    this.longPressTimer = null;
    this.isLongPress = false;

    this.internalBindTo(element);
  }

  /**
   * 清除所有的监听
   */
  cleanup() {
    const options = {
      passive: !this.preventDefault,
    };

    if (this.preventClick) {
      this.element.removeEventListener(
        "click",
        this.handlerPreventClick,
        options
      );
    }

    if (this.captureTouchmove) {
      this.element.removeEventListener(
        "touchmove",
        this.handlerTouchMove,
        options
      );
    }

    this.element.removeEventListener(
      "touchstart",
      this.handlerTouchStart,
      options
    );
    this.element.removeEventListener("touchend", this.handlerTouchEnd, options);
    this.element.removeEventListener("mousedown", this.handlerTouchStart, options);
    this.element.removeEventListener("mouseup", this.handlerTouchEnd, options);

    this.click.removeAll();
    this.touchstart.removeAll();
    this.touchend.removeAll();
    this.touchmove.removeAll();

    this.element = null;
  }

  /**
   * 监听元素事件
   * @param {HTMLElement} element
   */
  internalBindTo(element) {
    const options = {
      passive: !this.preventDefault,
    };

    this.handlerTouchStart = this.internalPointerDown.bind(this);
    this.handlerTouchEnd = this.internalPointerEnd.bind(this);
    this.handlerTouchMove = this.internalPointerMove.bind(this);
    this.handlerTouchOut = this.internalPointerOut.bind(this);

    if (this.preventClick) {
      this.handlerPreventClick = this.internalPreventClick.bind(this);
      element.addEventListener("click", this.handlerPreventClick, options);
    }

    if (this.captureTouchmove || this.longPressMs > 0) {
      element.addEventListener("touchmove", this.handlerTouchMove, options);
      element.addEventListener("mousemove", this.handlerTouchMove, options);
    }

    element.addEventListener("mousedown", this.handlerTouchStart, options);
    element.addEventListener("mouseup", this.handlerTouchEnd, options);
    element.addEventListener('mouseout', this.handlerTouchOut, options);
    element.addEventListener("touchstart", this.handlerTouchStart, options);
    element.addEventListener("touchend", this.handlerTouchEnd, options);

    this.element = element;
  }

  // 禁止点击事件
  internalPreventClick(ev) {
    window.focus();
    ev.preventDefault();
  }

  /**
   * 事件处理之前的处理
   * @param {*} ev
   * @param {*} expectedRemainingTouches 预期的触摸处个数
   */
  internalEventPreHandler(ev, expectedRemainingTouches = 1) {
    if (!this.element) {
      return false;
    }

    if (this.targetOnly && ev.target !== this.element) {
      return false;
    }

    if (this.preventDefault && ev.cancelable) {
      ev.preventDefault();
    }

    if (window.TouchEvent && ev instanceof TouchEvent) {
      clickDetectorGlobals.lastTouchTime = performance.now();

      if (ev.targetTouches.length !== expectedRemainingTouches) {
        return false;
      }
    }

    if(ev instanceof MouseEvent){
      if(performance.now() - clickDetectorGlobals.lastTouchTime < 1000){
        return false;
      }
    }

    return true;
  }

  /**
   * 获取触点的定位
   * @param {*} ev
   * @returns {Vector}
   */
  extractPointerPosition(ev) {
    if (window.TouchEvent && ev instanceof TouchEvent) {
      if (ev.changedTouches.length !== 1) {
        logger.warn(
          "正在运动的触点: ",
          ev.targetTouches.length,
          "->",
          ev.targetTouches
        );
        return new Vector(0, 0);
      }

      const touch = ev.changedTouches[0];
      return new Vector(touch.clientX, touch.clientY);
    }
    else if(window.MouseEvent && ev instanceof MouseEvent){
      return new Vector(ev.clientX, ev.clientY)
    }

    return new Vector(0, 0);
  }

  /**
   * 触点按下
   * @param {*} ev
   * @returns
   */
  internalPointerDown(ev) {
    if (!this.internalEventPreHandler(ev, 1)) {
      return false;
    }

    const position = this.extractPointerPosition(ev);

    this.touchstart.dispatch(position, ev);
    this.clickDownPosition = position;
    this.clickStartTime = performance.now();

    if (this.applyCssClass) {
      this.element.classList.add(this.applyCssClass);
    }

    if (this.clickSound) {
      GLOBAL_APP.sound.playUiSound(this.clickSound);
    }

    if(this.longPressMs > 0){
      // 长按
      this.longPressTimer = setTimeout(() => {
        this.isLongPress = true;
        this.longPress.dispatch(position, ev);
      }, this.longPressMs);
    }

    return false;
  }

  /**
   * 判断dom是否还在页面中
   * @returns
   */
  internalIsDomElementAttached() {
    return this.element && document.documentElement.contains(this.element);
  }

  /**
   * 触点松开
   * @param {*} ev
   * @returns
   */
  internalPointerEnd(ev) {
    if (!this.internalEventPreHandler(ev, 0)) {
      return false;
    }

    if (this.applyCssClass) {
      this.element.classList.remove(this.applyCssClass);
    }

    clearTimeout(this.longPressTimer);
    if(this.isLongPress){
      this.isLongPress = false;
      return;
    }

    let dispatchClick = false;
    let dispatchClickPos = null;
    const pos = this.extractPointerPosition(ev);

    if (this.clickDownPosition) {
      const distance = pos.distance(this.clickDownPosition);
      if (distance <= this.maxDistance) {
        dispatchClick = true;
        dispatchClickPos = pos;
      } else {
        // console.warn('[ClickDetector] 触点移动取消了点击');
      }
    }

    // 鼠标右键松开不属于点击事件
    if(ev instanceof MouseEvent && ev.button === 2){
      dispatchClick = false;
    }

    this.clickDownPosition = null;
    this.clickStartTime = null;

    if (this.internalIsDomElementAttached()) {
      this.touchend.dispatch(pos, ev);

      if (dispatchClick) {
        this.click.dispatch(dispatchClickPos, ev);
      }
    }

    return false;
  }

  /**
   * 触点移开
   * @param {*} ev 
   */
  internalPointerOut(ev){
    if (this.applyCssClass) {
      this.element.classList.remove(this.applyCssClass);
    }

    this.touchout.dispatch(ev);
    clearTimeout(this.longPressTimer);
    if(this.isLongPress){
      this.isLongPress = false;
      return;
    }

    return false;
  }

  /**
   * 触点移动
   * @param {*} ev
   */
  internalPointerMove(ev) {
    if (!this.internalEventPreHandler(ev, 1)) {
      return false;
    }

    const pos = this.extractPointerPosition(ev);
    if(this.clickDownPosition){
      const distance = pos.distance(this.clickDownPosition);
      if (distance > this.maxDistance) {
        clearTimeout(this.longPressTimer)
      }
    }

    this.touchmove.dispatch(pos, ev);
    return false;
  }
}
